package main

import (
	"fmt"
	"log"
	"math/rand"
	"net"
	"os"
	"time"

	"github.com/golang/protobuf/proto"
)

type User struct {
	Gid   uint32
	Uid   uint32
	Sign  string
	Addr  string
	Cnt   uint32
	Con   net.Conn
	Codec *Codec
}

func NewUser(Gid, Uid, Cnt uint32, Sign, Addr string) *User {
	return &User{
		Gid:  Gid,
		Uid:  Uid,
		Cnt:  Cnt,
		Sign: Sign,
		Addr: Addr,
	}
}

func (this *User) Auth() error {
	c, err := net.Dial("tcp", this.Addr)
	if err != nil {
		return err
	}
	this.Con = c
	this.Codec = NewCodec(c, GC_ReadBuffer, GC_WriteBuffer)

	// CC_Auth
	frameAuth := &Frame{Proto: CC_Auth}
	frameAuth.Content, err = proto.Marshal(&AuthRequest{Sign: this.Sign})
	if err != nil {
		return err
	}
	err = this.Codec.Write(frameAuth)
	if err != nil {
		return err
	}

	// CC_Ready
	frameReady := &Frame{Proto: CC_Ready}
	err = this.Codec.Write(frameReady)
	if err != nil {
		return err
	}
	return nil
}

func (this *User) Forward() error {
	frameBegin, err := this.Codec.Read()
	if err != nil {
		return err
	}

	fr, err := os.Create(fmt.Sprintf("%d_r.log", this.Uid))
	if err != nil {
		return err
	}
	defer fr.Close()
	loggerfr := log.New(fr, "", 0)

	fs, err := os.Create(fmt.Sprintf("%d_s.log", this.Uid))
	if err != nil {
		return err
	}
	defer fs.Close()
	loggerfs := log.New(fs, "", 0)

	if frameBegin.Proto != CC_Begin {
		return fmt.Errorf("Expected CC_Begin Got Proto:%d", frameBegin.Proto)
	}

	ma := &MessageArray{}
	for i := uint32(0); i < this.Cnt; i++ {
		data := []byte(fmt.Sprintf("%d", rand.Int()))

		loggerfs.Printf("%02d %d %d %s",
			ma.Seq+1, this.Gid, this.Uid, string(data))

		frameSend := this.Codec.GetFrame()
		frameSend.Proto = CC_Forward
		frameSend.Content, err = proto.Marshal(&Message{Data: data})
		if err != nil {
			return fmt.Errorf("Marshal Proto Err:%v", err)
		}
		err = this.Codec.Write(frameSend)
		if err != nil {
			return fmt.Errorf("Codec Write Err:%v", err)
		}
		this.Codec.PutFrame(frameSend)

		frame, err := this.Codec.Read()
		if err != nil {
			return fmt.Errorf("Read From Forwarder Err:%v", err)
		}

		if frame.Proto != CC_Broadcast {
			return fmt.Errorf("Expected CC_Broadcast Got Proto:%d", frame.Proto)
		}

		err = proto.Unmarshal(frame.Content, ma)
		if err != nil {
			return fmt.Errorf("Unmarshal Proto Err:%v", err)
		}

		for i := range ma.Datas {
			loggerfr.Printf("%02d %d %d %s",
				ma.Seq, ma.Datas[i].Gid, ma.Datas[i].Uid, string(ma.Datas[i].Data))
		}

		this.Codec.PutFrame(frame) // recycle
	}

	time.Sleep(100 * time.Millisecond)

	// TODO: release connection
	frameEnd := &Frame{Proto: CC_End}
	this.Codec.Write(frameEnd)

	log.Println("Forward Complete")

	return nil
}
